﻿using AJWPFAdmin.Core.Components.AJTable.ViewModels;
using AJWPFAdmin.Core.Crypto.AES;
using AJWPFAdmin.Core.Crypto.RSA;
using AJWPFAdmin.Core.Crypto.SMCrypto;
using AJWPFAdmin.Core.Enums;
using AJWPFAdmin.Core.ExtensionMethods;
using AJWPFAdmin.Core.Logger;
using AJWPFAdmin.Core.Models;
using AJWPFAdmin.Core.Models.EF.Tables;
using AJWPFAdmin.Core.Utils;
using AJWPFAdmin.Services.EF;
using AJWPFAdmin.Services.EF.TableExts;
using AngleSharp.Io;
using Azure;
using com.github.xiangyuecn.rsacsharp;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using Masuit.Tools.Reflection;
using Masuit.Tools.Security;
using MaterialDesignThemes.Wpf;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using Prism.Ioc;
using Quartz.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Reflection.Emit;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Interop;

namespace AJWPFAdmin.Services.AppointAPI
{
    public class LinyiAppointAPI : AppointAPIBase
    {
        private APIConfig _apiConfig;

        private System.Timers.Timer _deviceStatusTimer;

        private static readonly string[] _imageFields = new[] { "vehicleListImg", "inOutImg",
            "drivingLicenseImg",
            "vehicleNameplateImg",
            "engineNameplateImg",
            "mechanicalHbLabelImg" };

        //private static readonly Dictionary<CarPlateColor, string> _customCarPlateColorMapps = new Dictionary<CarPlateColor, string>
        //{
        //    {CarPlateColor.蓝牌, "0"},
        //    {CarPlateColor.黄牌, "1"},
        //    {CarPlateColor.白牌, "2"},
        //    {CarPlateColor.黑牌, "3"},
        //    {CarPlateColor.新能源绿牌, "4"},
        //    {CarPlateColor.其他, "5"},
        //    {CarPlateColor.新能源黄牌, "6"},

        //};

        public LinyiAppointAPI(ApplicationType appType,
            SystemConfigDictionary cfg,
            IContainerProvider svcProvider) : base(appType, cfg, svcProvider)
        {

        }

        protected override void OnAPILogsRaised(PassagewayDirection? direction,
            string logs)
        {
            base.OnAPILogsRaised(direction, logs);
        }

        public override Task<ProcessResult> UploadCarInfoAsync(CarInfoParams @params)
        {
            var result = new ProcessResult();

            var carType = (CarTypeEnum)((int)@params.CarInfo.TypeId);

            if (carType == CarTypeEnum.进出厂车)
            {
                result.SetSuccess();
                return Task.FromResult(result);
            }

            var processRet = ProcessShippingRecordToUpload(new ShippingRecordParams
            {
                Record = new ShippingRecord
                {
                    Attachments = @params.CarInfo.Attachments,
                    VehicleLicense = @params.CarInfo.VehicleLicense,
                    EngineNameplate = @params.CarInfo.EngineNameplate,
                    Nameplate = @params.CarInfo.Nameplate,
                    MachineEnvProtectionLabel = @params.CarInfo.MachineEnvProtectionLabel
                }
            }, PassagewayDirection.进);

            if (string.IsNullOrEmpty(processRet.method))
            {
                result.SetSuccess();

                return Task.FromResult(result);
            }

            return PostAsync(processRet.method, processRet.body, null,
                needUILog: true,
                direction: @params.Passageway?.Direction);
        }

        public override async Task<ProcessResult> UploadShippingRecordAsync(ShippingRecordParams @params)
        {
            ProcessResult result;
            var response = new UploadShippingRecordResponse();

            var carType = (CarTypeEnum)((int)@params.Record.TypeId);

            // 超过固定车范围的不做操作
            if (carType >= CarTypeEnum.固定车)
            {
                result = new ProcessResult();
                response.RecordNum++;
                result.SetSuccess();
                result.Attach = response;
                return result;
            }

            var processRet = ProcessShippingRecordToUpload(@params, @params.Record.Direction);
            if (string.IsNullOrEmpty(processRet.method))
            {
                result = new ProcessResult();
                result.SetSuccess();
                result.Attach = response;
                return result;
            }

            result = await PostAsync(processRet.method, processRet.body, null,
                needUILog: true,
                direction: @params.Record.Direction);

            if (!result.Success)
            {
                result.Attach = response;
                return result;
            }

            // 过车记录上传成功,记录号加1
            response.RecordNum++;
            result.Attach = response;

            return result;

        }

        private (string method, JObject body) ProcessShippingRecordToUpload(ShippingRecordParams @params, PassagewayDirection direction = PassagewayDirection.进)
        {

            JObject body = null;
            var method = string.Empty;

            // 超过固定车范围的不做操作
            if (@params.Record.TypeId > (long)CarTypeEnum.固定车)
            {
                return (method, body);
            }

            var rcdNum = direction == PassagewayDirection.进
                ? @params.Record.RecordNum - 1
                : @params.Record.RecordNum + 1;

            if (rcdNum <= 0)
            {
                rcdNum = 1;
            }

            var fakeCarInfo = new Car
            {
                Attachments = @params.Record.Attachments,
                VehicleLicense = @params.Record.VehicleLicense,
                EngineNameplate = @params.Record.EngineNameplate,
                Nameplate = @params.Record.Nameplate,
                MachineEnvProtectionLabel = @params.Record.MachineEnvProtectionLabel
            };

            var carNo = @params.Record.CarNo;

            var onboardListImage = fakeCarInfo.GetFileOfPropertyName(nameof(Car.Attachments));

            var drivingLicenseImage = fakeCarInfo.GetFileOfPropertyName(nameof(Car.VehicleLicense));

            if (@params.Record.TypeId == (long)CarTypeEnum.厂内运输车)
            {
                method = " /v1/api/inPlantVehicle";
                body = new JObject
                {
                    {"enterpriseNo",_apiConfig.EnterpriseNo },
                    {"hbRegisterCode",@params.Record.EnvRegCode },
                    {"VIN",@params.Record.VIN },
                    {"productionDate",@params.Record.ProductionDate.GetValueOrDefault().ToString("yyyy-MM-dd") },
                    {"plateNo",@params.Record.CarNo },
                    {"registerDate",@params.Record.RegDate.GetValueOrDefault().ToString("yyyy-MM-dd") },
                    {"vehicleBrandModel",@params.Record.CarBrandModel },
                    {"fuelType",@params.Record.FuelTypeName },
                    {"emissionStandard",@params.Record.EmissionStandard.GetDescription() },
                    {"networkStatus",@params.Record.NetworkingStatus ? "1" : "0"},
                    {"vehicleListImg", CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, onboardListImage), needHeader: false)},
                    {"drivingLicenseImg", CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, drivingLicenseImage), needHeader: false)},
                    {"vehicleOwner",  @params.Record.Owner}
                };
            }
            else if (@params.Record.TypeId == (long)CarTypeEnum.非道路移动机械)
            {
                method = "/v1/api/nonRoadMobileMachinery";

                var engineNameplate = fakeCarInfo.GetFileOfPropertyName(nameof(Car.EngineNameplate));

                var carNameplate = fakeCarInfo.GetFileOfPropertyName(nameof(Car.Nameplate));

                var vehicleLable = fakeCarInfo.GetFileOfPropertyName(nameof(Car.MachineEnvProtectionLabel));


                body = new JObject
                {
                    {"enterpriseNo",_apiConfig.EnterpriseNo },
                    {"hbRegisterCode",@params.Record.EnvRegCode },
                    {"productionDate",@params.Record.ProductionDate.GetValueOrDefault().ToString("yyyy-MM-dd") },
                    {"plateNo",@params.Record.CarNo },
                    {"fuelType",@params.Record.FuelTypeName },
                    {"emissionStandard",@params.Record.EmissionStandard.GetDescription() },
                    {"mechanicalType",(byte)(@params.Record.MachineType.GetValueOrDefault()) },
                    {"PIN",@params.Record.PIN },
                    {"mechanicalModel",@params.Record.MachineModel },
                    {"engineModel",@params.Record.EngineModel },
                    {"engineManufacturer",@params.Record.EngineManufacturer },
                    {"engineNo",@params.Record.EngineNo },
                    {"vehicleNameplateImg",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, carNameplate), needHeader: false)},
                    {"engineNameplateImg",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, engineNameplate), needHeader: false)},
                    {"mechanicalHbLabelImg",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, vehicleLable), needHeader: false)},
                    {"vehicleOwner",  @params.Record.Owner}
                };
            }
            else if (@params.Record.TypeId == (long)CarTypeEnum.进出厂车)
            {
                method = "/v1/api/inOutVehicle";
                //api = @params.Operation == DataOperationType.更新 ? "cargoLedger" : "passingRecord";

                var (carHead, _, carTail) = @params.Record.GetWatchPositionImageFile(@params.Record.Direction);

                body = new JObject
                    {
                        {"enterpriseNo",_apiConfig.EnterpriseNo },
                        {"entranceExitNo",direction == PassagewayDirection.进
                                ?  @params.Record.WatchhouseCode : @params.Record.OutWatchhouseCode },

                        {"gateNo",direction == PassagewayDirection.进

                                ? @params.Record.PassagewayCode :  @params.Record.OutPassagewayCode},

                        {"inOutStatus",direction == PassagewayDirection.进 ? "1" : "2"},
                        {"inOutTime",direction == PassagewayDirection.进
                                ? @params.Record.ShipStartDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss")
                                : @params.Record.ShipEndDate.GetValueOrDefault().ToString("yyyy-MM-dd HH:mm:ss") },
                        {"inOutImg",  CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, carHead), needHeader: false)},
                        {"plateNo",  @params.Record.CarNo},
                        {"plateColor",  ((byte)@params.Record.PlateColor).ToString()},
                        {"vehicleType",  @params.Record.TruckTypeName},
                        {"VIN",  @params.Record.VIN},
                        {"registerDate",@params.Record.RegDate.GetValueOrDefault().ToString("yyyy-MM-dd")},
                        {"vehicleBrandModel",@params.Record.CarBrandModel},
                        {"fuelType",@params.Record.FuelTypeName},
                        {"emissionStandard",@params.Record.EmissionStandard.GetDescription()},
                        {"networkStatus",@params.Record.NetworkingStatus ? 1 : 0},
                        {"useNature",@params.Record.UsageDescription},
                        {"vehicleListImg", CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, onboardListImage), needHeader: false)},
                        {"drivingLicenseImg", CommonUtil.GetBase64FromLocalOrHttpFile(_carIdentificationConfig.CompleteFileUrl(carNo, drivingLicenseImage), needHeader: false)},
                        {"transportGoods", direction == PassagewayDirection.进 ? @params.Record.InGoodsName : @params.Record.OutGoodsName},
                        {"transportVolume", direction == PassagewayDirection.进 ? @params.Record.InGoodsVolume.ToString()
                        : @params.Record.OutGoodsVolume.ToString()},
                        {"teamName",@params.Record.TeamName},
                    };
            }
            return (method, body);
        }

        private async Task<ProcessResult> PostAsync(
            string method,
            JObject body,
            Dictionary<string, string> query,
            bool isListResponse = false, bool needUILog = false,
            PassagewayDirection? direction = null)
        {
            var result = new ProcessResult();

            StringBuilder stringBuilder = null;

            using var httpClient = CreateClient();

            try
            {

                stringBuilder = new StringBuilder();

                var bodyStr = body == null ? "{}" : body.ToString(Newtonsoft.Json.Formatting.None);
                var queryStr = string.Empty;
                if (query?.Count > 0)
                {
                    queryStr = $"?{string.Join("&", query.Select(p => $"{p.Key}={HttpUtility.UrlEncode(p.Value)}"))}";
                }

                stringBuilder.Append($"{KeyDescription} {_apiConfig.APIUrl}{method}:\r\n");

                var logBody = new JObject();

                if (body != null)
                {
                    var props = body.Properties();
                    foreach (var prop in props)
                    {
                        if (_imageFields.Contains(prop.Name))
                        {
                            logBody.Add(prop.Name, prop.Value != null ? "图片数据" : prop.Value);
                        }
                        else
                        {
                            logBody.Add(prop.Name, prop.Value);
                        }
                    }
                }

                stringBuilder.Append($"请求body参数:{logBody.ToString()}\r\n");
                stringBuilder.Append($"请求查询参数:{queryStr}\r\n");

                var content = new StringContent(bodyStr, Encoding.UTF8, "application/json");
                //content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

                //var req = new HttpRequestMessage(System.Net.Http.HttpMethod.Post, $"{_apiConfig.APIUrl}{method}{queryStr}");
                //req.Headers.Add("Content-Type", "application/json");
                //req.Headers.Add("Authorization", $"Bearer {_apiConfig.Token}");
                //req.Content = content;
                //var res = await httpClient.SendAsync(req);
                //res.EnsureSuccessStatusCode();
                var res = await httpClient.PostAsync($"{_apiConfig.APIUrl}{method}{queryStr}", content);

                var responseStr = await res.Content.ReadAsStringAsync();

                stringBuilder.Append($"接口响应源内容\r\n:{responseStr}");

                if (res.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    var response = CommonUtil.TryGetJSONObject<APIResponse>(responseStr) ?? new APIResponse
                    {
                        code = -1,
                        msg = "解析接口返回失败",
                    };

                    if (response.code == 10200)
                    {
                        result.SetSuccess();
                    }
                    else
                    {
                        result.SetError(response.msg);
                    }

                }
                else
                {
                    result.SetError($"接口访问失败:{res.StatusCode}");
                }

                var logContent = stringBuilder.ToString();
                _logger.Error(logContent);
                if (needUILog)
                {
                    OnAPILogsRaised(direction, logContent);
                }

            }
            catch (Exception e)
            {
                var logContent = e.Message;
                stringBuilder.Append($"{logContent}\r\n");
                result.SetError(logContent, e);
                if (stringBuilder != null)
                {
                    stringBuilder.Append(e.StackTrace);
                    _logger.Error(stringBuilder.ToString());
                }
                if (needUILog)
                {
                    OnAPILogsRaised(direction, stringBuilder.ToString());
                }
            }
            return result;
        }

        private HttpClient CreateClient()
        {
            var httpClient = new HttpClient();

            httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Authorization", $"Bearer {_apiConfig.Token}");
            httpClient.DefaultRequestHeaders.Host = "localhost";
            httpClient.DefaultRequestHeaders.UserAgent.Clear();
            httpClient.DefaultRequestHeaders.UserAgent.Add(new System.Net.Http.Headers.ProductInfoHeaderValue("LinyiPAI", "1.0"));
            return httpClient;
        }

        public override Task ConvertToCustomConfig(bool isReal)
        {
            _apiConfig = CommonUtil.TryGetJSONObject<APIConfig>(Config.StringValue) ?? new APIConfig();
            _customAPIConfig = _apiConfig;
            AutoUploadDataVerification = _apiConfig.AutoUploadDataVerification;

            if (isReal)
            {
                if (_appType == ApplicationType.岗亭端)
                {
                    // 开启一个定时器, 每隔5分钟上传视频设备状态
                    _deviceStatusTimer = new System.Timers.Timer
                    {
                        AutoReset = true,
                        Interval = TimeSpan.FromMinutes(5).TotalMilliseconds,
                    };
                    _deviceStatusTimer.Elapsed += DeviceHeartbeatTimerHandler;
                    _deviceStatusTimer.Start();
                }
            }
            else
            {
                _ = GetSchemas();
            }

            return Task.CompletedTask;
        }

        private async void DeviceHeartbeatTimerHandler(object sender, System.Timers.ElapsedEventArgs e)
        {
            // 这里必须重新创建个dbcontent,因为在定时器线程中, 和全局的dbcontext隔离开
            var db = DbService.GetDbContext(_configService);

            var devices = db.Devices.Where(p => p.Synchronized).AsNoTracking().Select(p => new
            {
                p.Code,
                p.WatchhouseId,
                p.PassagewayId,
                p.OnlyMonitor
            }).ToList();

            var watchhouseSource = db.Watchhouses.Select(p => new
            {
                p.Id,
                p.Code
            }).ToList();

            var passagewaySource = db.Passageways.Select(p => new
            {
                p.Id,
                p.Code
            }).ToList();

            db.Dispose();

            var tasks = new List<Task<ProcessResult>>();

            foreach (var device in devices)
            {
                var result = await PostAsync("/v1/api/deviceStatus", new JObject
                {
                    {"enterpriseNo",_apiConfig.EnterpriseNo },
                    {"deviceType",device.OnlyMonitor ? 2 : 1 },
                    {"entranceExitNo",watchhouseSource
                    .FirstOrDefault(p=>p.Id == device.WatchhouseId)?.Code ?? string.Empty },
                    {"gateNo",passagewaySource
                    .FirstOrDefault(p=>p.Id == device.PassagewayId)?.Code ?? string.Empty },
                    {"time", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}
                }, null);
                if (!result.Success)
                {
                    _logger.Error($"{KeyDescription} 视频设备 {device.Code} 心跳传输失败:{result.Message}");
                }
            }
        }

        public new void Dispose()
        {
            _deviceStatusTimer?.Stop();
        }

        public class APIConfig : APIConfigBase
        {
            [DynamicFormItemDefine("接口地址")]
            public string APIUrl { get; set; }

            [DynamicFormItemDefine("企业编号")]
            public string EnterpriseNo { get; set; }

            [DynamicFormItemDefine("授权Token")]
            public string Token { get; set; }

        }

        public class APIResponse
        {
            public int code { get; set; }

            public string msg { get; set; }
            public object data { get; set; }
        }
    }
}
